Mapping fractions

setwd("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs")
Warning: The working directory was changed to /data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
file_paths <- list.files(pattern = "\\.readspercell\\.txt$", recursive=T)
file_paths
[1] "new/zUMIs_output/stats/OldNewPTO_new.readspercell.txt" "old/zUMIs_output/stats/OldNewPTO_old.readspercell.txt"
[3] "PTO/zUMIs_output/stats/OldNewPTO_PTO.readspercell.txt"
project <- str_extract(file_paths, "(...)(?=\\.readspercell\\.txt)")
names <- project
rpc_all_smpl <- data.frame(RG=character(),
                      N=integer(),
                      type=character(),
                      project=numeric(),
                      fraction=numeric(),
                      fraction_Type_project=numeric())


for (i in 1:length(file_paths)){
  
  rpc <- read.csv(file_paths[i], sep= "\t") %>%
    filter(RG != "bad") %>%
    mutate(project = project[i]) %>%
    group_by(RG) %>%
    mutate(sum = sum(N)) %>%
    ungroup() %>%
    mutate(fraction = N/sum) %>%
    group_by(project) %>%
    mutate(sum_project = sum(N)) %>%
    ungroup() %>%
    group_by(type) %>%
    mutate(sum_type = sum(N)) %>%
    ungroup() %>%
    mutate(fraction_type_project = sum_type/sum_project) %>%
    select(c(RG, N, type, project, fraction, fraction_type_project))
  
  
  rpc_all_smpl <- rbind(rpc_all_smpl, rpc)
  
}
rpc_all_smpl <- rpc_all_smpl %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO")))


means <- rpc_all_smpl %>%
  select(c(type, project, fraction_type_project)) %>%
  distinct()

mapping_fract_reps <- ggplot(data=rpc_all_smpl, aes(y=fraction, x=type, color=type))+
  geom_boxplot()+
  #geom_text(aes(label=fraction_type_project), nudge_y=60000)+
  theme_minimal()+
  ylab("Fraction of reads")+
  xlab("")+
  coord_flip()+
  theme_Publication()+
  # theme(axis.text  = element_text(size=13),
  #       axis.title  = element_text(size=14),
  #       strip.text.x = element_text(size = 14),
  #       panel.background = element_blank(),
  #       axis.line = element_line(colour = "black"),
  #       #panel.grid.major = element_blank(),
  #       #panel.grid.minor = element_blank(),
  #       panel.border = element_rect(colour = "black", fill=NA, size=1),
  #       #text = element_text(family = "Arial"),
  #       legend.position = "none"
  # )+
  theme(legend.position = "none") +
  facet_grid(.~project) + 
  geom_text(data = means, aes(label = paste0(round(fraction_type_project*100, 0), "%"), 
                              y = fraction_type_project + 0.1 + fraction_type_project * 0.05))+ 
  scale_color_aaas()
Warning: The `size` argument of `element_rect()` is deprecated as of ggplot2 3.4.0.
Please use the `linewidth` argument instead.
mapping_fract_reps


# ggsave(plot=mapping_fract_reps, 
#        filename = "/data/share/htp/prime-seq_NextGen/figures/fig_1_mapping_fractions.pdf",
#        height = 3,
#        width = 6)

new & PTO quite similar; both higher Intron, Exon, Ambiguity, lower Intergenic, Unmapped

Total reads

Assigned index

ass_reads <- read.delim(file="/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/deML_summary_to_read_in.txt") %>%
  select(RG, assigned, total) %>%
  dplyr::rename("project"="RG") %>%
  filter(str_detect(project, "primeseq")) %>%
  mutate(project = str_extract(project, "...$")) %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO"))) %>%
  mutate(non_assigned = total-assigned, 
         fract_non_assigned = paste(round(non_assigned/total, digits=3)*100, "%")) %>%
  pivot_longer(cols=c(2,4), names_to = "category", values_to = "reads") %>%
  mutate(fract_non_assigned = ifelse(category == "non_assigned", fract_non_assigned, NA))
  
ass_reads %>%
  ggplot(aes(y=reads, x=project, fill=category)) +
  geom_col()+
  geom_text(aes(label=fract_non_assigned))+
  xlab("")+
  ylab("Total Reads")+
  labs(fill="Index deML")

NA
NA

PTO > new > old assigned fractions same

Assigned barcodes

# get data
setwd("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/")
Warning: The working directory was changed to /data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
file_paths1 <- list.files(pattern = "kept_barcodes_binned\\.txt$", recursive=T)
file_paths1
[1] "new/zUMIs_output/OldNewPTO_newkept_barcodes_binned.txt" "old/zUMIs_output/OldNewPTO_oldkept_barcodes_binned.txt"
[3] "PTO/zUMIs_output/OldNewPTO_PTOkept_barcodes_binned.txt"
nreads <- data.frame(sample = NA,
                     reads = NA)

nreads_barcodes <- data.frame(xc = character(), 
                              n = integer(),
                              cellindex = integer(),
                              project=character())

for (i in 1:length(file_paths1)){
  
  barcodes <- read.csv(file_paths1[i])
  barcodes$project <- names[i]
  nreads[i,1] <- names[i]
  nreads[i,2] <- sum(barcodes$n)
  nreads_barcodes <- rbind(nreads_barcodes, barcodes)
  

}
nreads_barcodes <- nreads_barcodes %>%
  group_by(project) %>%
  mutate(sum = sum(n)) %>%
  ungroup() %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO")))
# -----------------------------------------------------------------------

# plot overall reads / sample
ggplot(data=nreads, aes(y=reads, x=sample, fill=project)) +
  geom_bar(stat="identity") +
  coord_flip() +
  theme_minimal()+
  ylab("Reads with assigned Barcode")+
  xlab("")+
  theme(legend.position = "none")


# plot in a different layout
ggplot(data=nreads_barcodes, aes(y=n, x=project, color=project))+
  #geom_boxplot(outlier.shape = NA)+
  geom_point()+
  coord_flip()+
  stat_summary(fun.data = "mean_cl_boot")+
  ylab("Reads with assigned Barcode")+
  xlab("")+
  theme(legend.position = "none")

NA
NA

mostly depended on overall reads

Compare assigned barcode to assigned index

nreads_barcodes %>%
  select(-c(XC, n, cellindex)) %>%
  left_join(ass_reads %>% filter(category == "assigned") %>% select(project, reads), by="project") %>%
  distinct() %>%
  mutate(fraction_with_bc = sum / reads) %>%
  ggplot() +
  geom_point(aes(x = project, y = fraction_with_bc, color=project))+
  xlab("")+
  ylab("Assigned barcode \n ------------------------ \n Assigned index")+
  theme(legend.position = "none")

Unused barcoded

same as above

# df_all <- data.frame(true_BC=logical(),
#                      n=integer(),
#                      f=numeric(),
#                      project=character(),
#                      PS=character(),
#                      seq_adapters=character())
# 
# samples <- names
# for (i in samples){
#   seq=as.data.frame(readFastq(dirPath = "/data/share/htp/prime-seq_NextGen/data/FC2024_05_01_PoP96_Quanti/02_trimming",
#                               pattern = paste0("lane1_primeseq_PoP96_Quanti_", i, "_r1_trimmed.fq.gz"))@sread)
#   colnames(seq)=c("seq")
#   seq=seq %>%
#     mutate(seq=as.character(seq)) %>%
#     mutate(BC=substr(seq,1,12))
#   
#   BCs <- c(read.csv(file=paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_01_PoP96_Quanti/03_zUMIs/", 
#                                 i, "/zUMIs_output/OldNewPTO_", i, ".BCbinning.txt"), 
#                     sep=",")[,1],
#            read.csv(file=paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_01_PoP96_Quanti/03_zUMIs/", 
#                                 i, "/zUMIs_output/OldNewPTO_", i, "kept_barcodes.txt"),
#                     sep=",")[,1])
#   
#   df <- seq %>% dplyr::count(BC) %>% arrange(-n) %>%
#     mutate(true_BC = case_when(BC %in% BCs ~ TRUE,
#                                T ~ FALSE)) %>%
#     dplyr::count(true_BC, wt=n) %>%
#     mutate(f=n/sum(n)) %>%
#     mutate(project=i)
#   
#   df_all <- rbind(df_all, df)
# }
# 
# 
# df_all <- df_all %>%
#   mutate(project = factor(project, levels=c("old", "new", "PTO")))
# 
# ggplot(data=df_all %>% filter(true_BC==FALSE), aes(y=f, x=project))+
#   geom_col()+
#   ylab("Assi \n ------------------------ \n Assigned index")+
#   xlab("")
# 
# 
# # plot unused barcode reads
# # -> mostly affected by read no
# ggplot(data=df_all %>% filter(true_BC==FALSE), aes(y=n, x=project))+
#   geom_col()+
#   ylab("Reads")+
#   xlab("")

Complexity

source("/data/home/felix/Complexity.R")

samples <- names

setDTthreads(threads=30)

complexity <- data.frame(RG=character(),
                         n=integer(),
                         project=character(),
                         replicate=integer(),
                         ds_level=integer())

# reads: 4 490 276 - 5 952 002
for (k in c(10000000, 1000000)){
  print(k)
  for (i in samples){
    print(paste0("sample ", i))
    for (j in 1:3){
      print(paste0("rep ", j, " of 10"))
      inputBAM = paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/",
                        i,
                        "/OldNewPTO_",
                        i,
                        ".filtered.Aligned.GeneTagged.sorted.bam"
                        )
        bccount = paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/",
                         i,
                         "/zUMIs_output/OldNewPTO_",
                         i,
                         "kept_barcodes_binned.txt"
                         )
      bccount <- fread(bccount)
      bccount<-splitRG(bccount=bccount, mem= 50, hamdist = 0)
      reads <- reads2genes_new_ds(featfile = inputBAM,
                                  bccount  = bccount,
                                  inex     = TRUE,
                                  chunk    = 1,
                                  cores    = 20,
                                  downsampling = k)
      print(nrow(reads))
      reads <- reads %>% filter(!is.na(GE)) %>% dplyr::select(-UB, -ftype) %>% distinct() %>% dplyr::count(RG) %>% 
        mutate(project=i) %>%
        mutate(replicate=j) %>%
        mutate(ds_level=k)
      complexity <- rbind(complexity, reads)
      rm(reads)
    }
  }
}
saveRDS(complexity, "/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/complexity.rds")
complexity <- readRDS("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/complexity.rds")
complexity2 <- complexity %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO"))) %>%
  mutate(ds_level = ifelse(ds_level == 10000000, "no downsampling", ds_level)) %>%
  mutate(ds_level = factor(ds_level, levels = c("no downsampling", "1e+06")))

a <- ggplot(complexity2, aes(y=n, x=project, fill=project))+
  geom_violin()+
  coord_flip()+
  stat_summary(fun.data = "mean_cl_boot", geom = "pointrange",
               colour = "black")+
  facet_grid(~ds_level, scale="free")+
  xlab("")+
  ylab("Complexity (Detected Genes)")+
  theme_Publication()+
  theme(legend.position = "none")
a

Demultiplexing

# load data
deml <- read.delim(file="/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/deML_summary_to_read_in.txt") %>%
    dplyr::rename("project"="RG") %>%
  filter(str_detect(project, "primeseq")) %>%
  mutate(project = str_extract(project, "...$")) %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO"))) %>%
  mutate(total_fract = as.numeric(sub("%$", "", total.))/100) %>% select(-total.) %>%
  mutate(assigned_fract = as.numeric(sub("%$", "", assigned.))/100) %>% select(-assigned.) %>%
  mutate(unknown_fract = as.numeric(sub("%$", "", unknown.))/100) %>% select(-unknown.)%>%
  mutate(conflict_fract = as.numeric(sub("%$", "", conflict.))/100) %>% select(-conflict.) %>%
  mutate(wrong_fract = as.numeric(sub("%$", "", wrong.))/100) %>% select(-wrong.)

# make it long
deml_long <- deml %>% select(c(1, 7:11)) %>%
  pivot_longer(cols=2:6, names_to="category", values_to="fraction") %>%
  mutate(category=sub("_fract", "", category)) %>%
  mutate(fraction = as.numeric(fraction))%>%
  mutate(category = factor(category, levels=c("total", "conflict", "wrong", "unknown", "assigned")))

# what does what mean:
# "unknown": It's more likely that you belong to that sample than any other but that probability is low
# "conflict": It's more likely that you belong to that sample than any other but the probability that you belong to another sample is almost equally likely
# "wrong": It's more likely that you belong to that sample than any other but it seems your indices are mispaired

# plot
plot <- ggplot(deml_long %>% filter(category != "total"), aes(x = project, y=fraction, fill = category)) + 
  geom_bar(stat="identity")+ 
  #scale_x_discrete(limits=c("D2", "D0.5", "D1"))+
  xlab("")+
  ylab("Fraction of total reads")+
  labs(fill="Demultiplexing category")+
  scale_fill_manual(values = c("assigned" = "lightgreen", "unknown" = "grey70", "wrong" = "grey50", "conflict" = "grey30"))+
  # coord_cartesian(ylim = c(0.5, 1))+
  coord_flip()+
  theme_Publication()

plot

plot_cut <- plot+
  coord_flip(ylim = c(0.95, 1))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# ggsave(plot, filename="/data/share/htp/prime-seq_NextGen/figures/fig_1_demultiplexing.pdf", height=2.5, width=12)
# ggsave(plot_cut, filename="/data/share/htp/prime-seq_NextGen/figures/fig_1_demultiplexing_cut.pdf", height=2, width=6)

overrepresented seqs in read 2

combined_df <- data.frame()

for (n in names) {
  temp <- read.delim(
    paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/overrepresented/", 
           n, 
           ".out.txt")) %>%
    mutate(condition=n)
  
  combined_df <- bind_rows(combined_df, temp)%>%
  mutate(across(condition, factor, levels=c("old", "new", "PTO")))
}

combined_df %>%
  ggplot(aes(x=Sequence, y=Percentage))+
  geom_col(size=2)+
  facet_grid(condition~.)+
  coord_flip()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

both sequences get reduced, esp. GGGGG…

Filtering

grep -E “Total read pairs.|Read 2 with.|Pairs that.*” “$input_file”

trim_df <- data.frame()

for (n in names) {
  temp <- read_delim(paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/02_trimming/", 
                            n, 
                            ".txt"), 
                     col_names = c("category", "reads")) %>% 
    mutate(reads = str_replace_all(reads, " ", "")) %>% 
    mutate(reads = str_replace_all(reads, ",", "")) %>% 
    mutate(reads = str_replace_all(reads, "%\\)", "")) %>% 
    separate_wider_delim(reads, delim = "(", names = c("reads", "percentage"), too_few = "align_start") %>%
    mutate(condition=n)
  
  trim_df <- bind_rows(trim_df, temp)
}
Rows: 3 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ":"
chr (2): category, reads
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 3 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ":"
chr (2): category, reads
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 3 Columns: 2── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ":"
chr (2): category, reads
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
trim_df <- trim_df %>%
    mutate(across(condition, factor, levels=c("old", "new", "PTO"))) %>%
    mutate(reads = as.numeric(reads), percentage = as.numeric(percentage))


trim_df %>%
  filter(category != "Total read pairs processed") %>%
  ggplot(aes(y=percentage, x=category)) +
  geom_col(size=2)+
  facet_grid(condition~.)+
  coord_flip()

NA
NA

reduction from old to new in both slight increase from new to PTO in both

BC binning

bin_df <- data.frame()

for (na in names) {
  temp <- read_delim(paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/", 
                            na, 
                            "/zUMIs_output/OldNewPTO_", 
                            na, 
                            ".BCbinning.txt")) %>% 
    mutate(project=na)
  
  bin_df <- bind_rows(bin_df, temp)
}
Rows: 265 Columns: 4── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): falseBC, trueBC
dbl (2): hamming, n
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 284 Columns: 4── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): falseBC, trueBC
dbl (2): hamming, n
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 303 Columns: 4── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): falseBC, trueBC
dbl (2): hamming, n
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
bin_df <- bin_df %>%
    mutate(across(project, factor, levels=c("old", "new", "PTO")))


bin_df %>%
  group_by(project, hamming) %>%
  reframe(total=sum(n)) %>%
  ggplot()+
  geom_col(aes(x=project, y=total, fill=project))+
  facet_grid(.~hamming)+
  theme(legend.position = "none")+
  ggtitle("Barcodes binned by Hamming Distance")+
  ylab("Reads")+
  xlab("")


# as fraction of total reads
BC_binning_HD_plot <- bin_df %>%
  group_by(project, hamming) %>%
  reframe(total=sum(n)) %>%
  left_join(nreads, by=c("project" = "sample")) %>%
  mutate(bin_fraction = total/reads) %>%
    mutate(across(project, factor, levels=c("old", "new", "PTO"))) %>%
  ggplot()+
  geom_col(aes(x=project, y=bin_fraction, fill=project), position="dodge")+
    facet_grid(.~hamming)+
  # facet_grid(hamming~., scales="free")+
  ggtitle("Hamming Distance")+
  ylab(expression(frac('Reads binned', 'Assigned barcode reads')))+
  xlab("")+
  theme_Publication()+
  theme(legend.position = "none")

# ggsave(plot = BC_binning_HD_plot, 
#        filename = "/data/share/htp/prime-seq_NextGen/figures/fig_S_BC_binning.pdf",
#        width=7,
#        height=6)

# per BC
BC_binning_HD_plot

bin_df_BC <- bin_df %>%
  group_by(hamming, trueBC, project) %>%
  reframe(n_binned = sum(n)) %>%
  left_join(nreads_barcodes, by=c("project", "trueBC" = "XC")) %>%
  mutate(bin_fraction = n_binned/n) %>%
    mutate(across(project, factor, levels=c("old", "new", "PTO")))

BC_binning_HD_plot_2 <- bin_df_BC %>%
  ggplot()+
  geom_boxplot(aes(x=project, y=bin_fraction, fill=project), position="dodge")+
  #  facet_grid(.~hamming)+
   facet_grid(hamming~., scales="free")+
  ggtitle("Hamming Distance")+
  ylab(expression(frac('Reads binned', 'Assigned barcode reads')))+
  xlab("")+
  theme_Publication()+
  theme(legend.position = "none")

BC_binning_HD_plot_2


# ggsave(plot = BC_binning_HD_plot_2,
#        filename = "/data/share/htp/prime-seq_NextGen/figures/fig_S_BC_binning_by_BC.pdf",
#        width=4,
#        height=7)

# distribution
bin_df %>%
  left_join(nreads_barcodes %>% dplyr::rename("reads" = "n"), by=c("project", "trueBC" = "XC")) %>%
  group_by(hamming, trueBC, project) %>%
  summarise(n_fraction = sum(n) / sum(reads)) %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO"))) %>%
  ggplot()+
  geom_col(aes(y=n_fraction, x=reorder(trueBC, -n_fraction), fill=project))+
  facet_grid(hamming~project, scale="free")+ 
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))+
  ggtitle("Distribution of reads per barcode")+
  ylab(expression(frac('Reads binned', 'Assigned barcode reads')))+
  xlab("Barcodes")+
  theme(legend.position = "none")
`summarise()` has grouped output by 'hamming', 'trueBC'. You can override using the `.groups` argument.

bin_df %>%
  left_join(nreads_barcodes %>% dplyr::rename("reads" = "n"), by=c("project", "trueBC" = "XC")) %>%
  group_by(hamming, trueBC, project) %>%
  summarise(n_absolute = sum(n)) %>%
  mutate(across(project, factor, levels=c("old", "new", "PTO"))) %>%
  ggplot()+
  geom_col(aes(y=n_absolute, x=reorder(trueBC, -n_absolute), fill=project))+
  facet_grid(hamming~project, scale="free")+ 
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))+
  ggtitle("Distribution of reads per barcode")+
  ylab('Reads binned')+
  xlab("Barcodes")+
  theme(legend.position = "none")
`summarise()` has grouped output by 'hamming', 'trueBC'. You can override using the `.groups` argument.

Read / UMI count data

# read in:
read_umi <- data.frame()
for (i in names) {
  gene_counts <- read_rds(
    file=paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/", 
                i, 
                "/zUMIs_output/stats/OldNewPTO_", 
                i, 
                ".bc.READcounts.rds")
    ) %>%
    dplyr::rename(read_count = N, SampleID = RG)
  umi_counts <- read_delim(
    file=paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/", 
                i, 
                "/zUMIs_output/stats/OldNewPTO_", 
                i, 
                ".UMIcounts.txt")
    ) %>%
    dplyr::rename(umi_count = Count)
  read_umi <- rbind(read_umi, 
                    full_join(gene_counts, umi_counts, by=c("SampleID", "type")) %>%
                      mutate(project = i)
                    )
}
Rows: 30 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): SampleID, type
dbl (1): Count
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 33 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): SampleID, type
dbl (1): Count
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 33 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): SampleID, type
dbl (1): Count
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
read_umi_raw <- read_umi %>%
  mutate(project = factor(project, levels=c("old", "new", "PTO")))
# process
read_umi <- read_umi %>%
  filter(type %in% c("Exon", "Intron") & 
           SampleID != "bad") %>%
  mutate(umi_fraction = umi_count/read_count) %>%
  mutate(project = factor(project, levels=c("old", "new", "PTO")))

read_umi %>%
  ggplot(aes(y=umi_fraction, x=project, color=project))+
  geom_quasirandom()+
  stat_summary(fun = mean,
               geom = "crossbar")+
  facet_grid(~type)+
  theme(legend.position = "none")+
  ylab(expression(frac(UMIs, Reads)))

  
read_umi_summary <- read_umi %>%
  group_by(project) %>%
  summarise(UMI = sum(umi_count))

Funnel

read_funnel  <-
  ass_reads %>%
  filter(category == "assigned") %>%
  select(project, total, "index_assigned" = "reads") %>%
  full_join(
    trim_df %>%
      select(-percentage) %>%
      pivot_wider(names_from = category, values_from = reads) %>%
      group_by(condition) %>%
      mutate(trimmed = `Total read pairs processed` - `Pairs that were too short`) %>%
      select(project = condition, trimmed), by="project") %>%
  pivot_longer(cols=-1, names_to = "step", values_to = "reads") %>%
  bind_rows(
    nreads_barcodes %>% select(BC = XC, barcode_assigned = n, project) %>%
      full_join(rpc_all_smpl %>% 
                  select(BC = RG, N, type, project) %>%
                  ungroup() %>% 
                  group_by(project, BC) %>% 
                  filter(type %in% c("Intron", "Exon")) %>% 
                  summarise(inex=sum(N)),
                by=c("project", "BC")) %>%
      full_join(read_umi %>% 
                  select(BC = SampleID, UMI= umi_count, type, project) %>%
                  group_by(project, BC) %>%
                  filter(type %in% c("Intron", "Exon")) %>% 
                  summarise(UMI=sum(UMI)),
                by=c("project", "BC")) %>%
      pivot_longer(cols=-c("BC", "project"), names_to = "step", values_to = "reads")) %>%
  filter(project != "new") %>%
  mutate(project = ifelse(project == "PTO", "prime-seq-opti", "prime-seq")) %>%
  mutate(step = factor(step, levels = c("total", "index_assigned", "trimmed", "barcode_assigned", "inex", "UMI"))) %>%
  mutate(max = max(reads)) %>%
  group_by(project) %>%
  mutate(max_project = max(reads)) %>%
    group_by(project, BC) %>%
  mutate(fractions = reads/max_project) %>%
  mutate(fractions = ifelse(!is.na(BC), fractions*11, fractions)) %>%
  #mutate(fractions = ifelse(is.na(fractions), 1, fractions)) %>%
  mutate(fractions_rel_max = reads/max) %>%
  mutate(fractions_rel_max = ifelse(!is.na(BC), fractions_rel_max*11, fractions_rel_max))
`summarise()` has grouped output by 'project'. You can override using the `.groups` argument.`summarise()` has grouped output by 'project'. You can override using the `.groups` argument.
read_funnel %>% 
  ggplot()+
  geom_line(aes(y=reads, x=step, color=project, group=project))


read_funnel %>% 
  ggplot()+
  geom_line(aes(y=fractions, x=step, color=project, group=project))+
  ylab("Fraction of total")


read_funnel %>% 
  ggplot()+
  geom_line(aes(y=fractions_rel_max, x=step, color=project, group=project))+
  ylab("Fraction of max total")





read_funnel %>%  
  filter(project == "old") %>%
  plot_ly(
    type = "funnel",
    y = ~step,
    x = ~reads,
    textinfo = "value+percent initial")

read_funnel %>%  
  filter(project == "new") %>%
  plot_ly(
    type = "funnel",
    y = ~step,
    x = ~reads,
    textinfo = "value+percent initial")

read_funnel %>%  
  filter(project == "PTO") %>%
  plot_ly(
    type = "funnel",
    y = ~step,
    x = ~reads,
    textinfo = "value+percent initial")
NA

non-coding fractions —-

# read count
breakdown_reads <- map_df(names, function(i) {
  readRDS(paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/", 
                 i, 
                 "/zUMIs_output/stats/OldNewPTO_", 
                 i, 
                 ".breakdown_readcount.rds")) %>%
    mutate(project = i)
})
breakdown_reads %>%
  mutate(project = factor(project, levels = c("old", "new", "PTO")))%>%
  ggplot(aes(y=Fract, x=project, color=project)) + 
  geom_boxplot() +
  facet_wrap(~type, scales="free_y") +
  theme(legend.position="none")+
  ylab("Fraction of Reads")+
  xlab("")

  

# umi count
breakdown_umi <- map_df(names, function(i) {
  readRDS(paste0("/data/share/htp/prime-seq_NextGen/data/FC2024_05_02_PoP96_BA/03_zUMIs/", 
                 i, 
                 "/zUMIs_output/stats/OldNewPTO_", 
                 i, 
                 ".breakdown_umicount.rds")) %>%
    mutate(project = i)
})
breakdown_umi %>%
  mutate(project = factor(project, levels = c("old", "new", "PTO")))%>%
  ggplot(aes(y=Fract, x=project, color=project)) + 
  geom_boxplot() +
  facet_wrap(~type, scales="free_y") +
  theme(legend.position="none")+
  ylab("Fraction of UMIs")+
  xlab("")

Possible publication figures —-

LS0tCnRpdGxlOiAiUm91Z2ggbG9vayBhdCBvbGQgdnMgbmV3IHZzIFBUTyBwcmltZS1zZXEgZGF0YSAtIEJBIgpkYXRlOiAiMDMuMDYuMjAyNCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoU2hvcnRSZWFkKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2diZWVzd2FybSkKbGlicmFyeShnZ3NjaSkKbGlicmFyeShwdXJycikKc291cmNlKCIvZGF0YS9ob21lL2ZlbGl4L3NjcmlwdHNfYW5kX2Z1bmN0aW9ucy90aGVtZV9wdWJsaWNhdGlvbi5SIikKYGBgCgoKIyBNYXBwaW5nIGZyYWN0aW9ucwpgYGB7cn0Kc2V0d2QoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS8wM196VU1JcyIpCmZpbGVfcGF0aHMgPC0gbGlzdC5maWxlcyhwYXR0ZXJuID0gIlxcLnJlYWRzcGVyY2VsbFxcLnR4dCQiLCByZWN1cnNpdmU9VCkKZmlsZV9wYXRocwoKcHJvamVjdCA8LSBzdHJfZXh0cmFjdChmaWxlX3BhdGhzLCAiKC4uLikoPz1cXC5yZWFkc3BlcmNlbGxcXC50eHQpIikKbmFtZXMgPC0gcHJvamVjdApycGNfYWxsX3NtcGwgPC0gZGF0YS5mcmFtZShSRz1jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgIE49aW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgdHlwZT1jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgIHByb2plY3Q9bnVtZXJpYygpLAogICAgICAgICAgICAgICAgICAgICAgZnJhY3Rpb249bnVtZXJpYygpLAogICAgICAgICAgICAgICAgICAgICAgZnJhY3Rpb25fVHlwZV9wcm9qZWN0PW51bWVyaWMoKSkKCgpmb3IgKGkgaW4gMTpsZW5ndGgoZmlsZV9wYXRocykpewogIAogIHJwYyA8LSByZWFkLmNzdihmaWxlX3BhdGhzW2ldLCBzZXA9ICJcdCIpICU+JQogICAgZmlsdGVyKFJHICE9ICJiYWQiKSAlPiUKICAgIG11dGF0ZShwcm9qZWN0ID0gcHJvamVjdFtpXSkgJT4lCiAgICBncm91cF9ieShSRykgJT4lCiAgICBtdXRhdGUoc3VtID0gc3VtKE4pKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShmcmFjdGlvbiA9IE4vc3VtKSAlPiUKICAgIGdyb3VwX2J5KHByb2plY3QpICU+JQogICAgbXV0YXRlKHN1bV9wcm9qZWN0ID0gc3VtKE4pKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIGdyb3VwX2J5KHR5cGUpICU+JQogICAgbXV0YXRlKHN1bV90eXBlID0gc3VtKE4pKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIG11dGF0ZShmcmFjdGlvbl90eXBlX3Byb2plY3QgPSBzdW1fdHlwZS9zdW1fcHJvamVjdCkgJT4lCiAgICBzZWxlY3QoYyhSRywgTiwgdHlwZSwgcHJvamVjdCwgZnJhY3Rpb24sIGZyYWN0aW9uX3R5cGVfcHJvamVjdCkpCiAgCiAgCiAgcnBjX2FsbF9zbXBsIDwtIHJiaW5kKHJwY19hbGxfc21wbCwgcnBjKQogIAp9CnJwY19hbGxfc21wbCA8LSBycGNfYWxsX3NtcGwgJT4lCiAgbXV0YXRlKGFjcm9zcyhwcm9qZWN0LCBmYWN0b3IsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkKCgptZWFucyA8LSBycGNfYWxsX3NtcGwgJT4lCiAgc2VsZWN0KGModHlwZSwgcHJvamVjdCwgZnJhY3Rpb25fdHlwZV9wcm9qZWN0KSkgJT4lCiAgZGlzdGluY3QoKQoKbWFwcGluZ19mcmFjdF9yZXBzIDwtIGdncGxvdChkYXRhPXJwY19hbGxfc21wbCwgYWVzKHk9ZnJhY3Rpb24sIHg9dHlwZSwgY29sb3I9dHlwZSkpKwogIGdlb21fYm94cGxvdCgpKwogICNnZW9tX3RleHQoYWVzKGxhYmVsPWZyYWN0aW9uX3R5cGVfcHJvamVjdCksIG51ZGdlX3k9NjAwMDApKwogIHRoZW1lX21pbmltYWwoKSsKICB5bGFiKCJGcmFjdGlvbiBvZiByZWFkcyIpKwogIHhsYWIoIiIpKwogIGNvb3JkX2ZsaXAoKSsKICB0aGVtZV9QdWJsaWNhdGlvbigpKwogICMgdGhlbWUoYXhpcy50ZXh0ICA9IGVsZW1lbnRfdGV4dChzaXplPTEzKSwKICAjICAgICAgIGF4aXMudGl0bGUgID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLAogICMgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgIyAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICMgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLAogICMgICAgICAgI3BhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgIyAgICAgICAjcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAjICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BLCBzaXplPTEpLAogICMgICAgICAgI3RleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkFyaWFsIiksCiAgIyAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICAjICkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZmFjZXRfZ3JpZCgufnByb2plY3QpICsgCiAgZ2VvbV90ZXh0KGRhdGEgPSBtZWFucywgYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKGZyYWN0aW9uX3R5cGVfcHJvamVjdCoxMDAsIDApLCAiJSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGZyYWN0aW9uX3R5cGVfcHJvamVjdCArIDAuMSArIGZyYWN0aW9uX3R5cGVfcHJvamVjdCAqIDAuMDUpKSsgCiAgc2NhbGVfY29sb3JfYWFhcygpCm1hcHBpbmdfZnJhY3RfcmVwcwoKIyBnZ3NhdmUocGxvdD1tYXBwaW5nX2ZyYWN0X3JlcHMsIAojICAgICAgICBmaWxlbmFtZSA9ICIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZmlndXJlcy9maWdfMV9tYXBwaW5nX2ZyYWN0aW9ucy5wZGYiLAojICAgICAgICBoZWlnaHQgPSAzLAojICAgICAgICB3aWR0aCA9IDYpCmBgYApuZXcgJiBQVE8gcXVpdGUgc2ltaWxhcjsgCmJvdGggCiAgaGlnaGVyIEludHJvbiwgRXhvbiwgQW1iaWd1aXR5LCAKICBsb3dlciBJbnRlcmdlbmljLCBVbm1hcHBlZAoKCiMgVG90YWwgcmVhZHMKIyMgQXNzaWduZWQgaW5kZXgKYGBge3J9CmFzc19yZWFkcyA8LSByZWFkLmRlbGltKGZpbGU9Ii9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS9kZU1MX3N1bW1hcnlfdG9fcmVhZF9pbi50eHQiKSAlPiUKICBzZWxlY3QoUkcsIGFzc2lnbmVkLCB0b3RhbCkgJT4lCiAgZHBseXI6OnJlbmFtZSgicHJvamVjdCI9IlJHIikgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QocHJvamVjdCwgInByaW1lc2VxIikpICU+JQogIG11dGF0ZShwcm9qZWN0ID0gc3RyX2V4dHJhY3QocHJvamVjdCwgIi4uLiQiKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhwcm9qZWN0LCBmYWN0b3IsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkgJT4lCiAgbXV0YXRlKG5vbl9hc3NpZ25lZCA9IHRvdGFsLWFzc2lnbmVkLCAKICAgICAgICAgZnJhY3Rfbm9uX2Fzc2lnbmVkID0gcGFzdGUocm91bmQobm9uX2Fzc2lnbmVkL3RvdGFsLCBkaWdpdHM9MykqMTAwLCAiJSIpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scz1jKDIsNCksIG5hbWVzX3RvID0gImNhdGVnb3J5IiwgdmFsdWVzX3RvID0gInJlYWRzIikgJT4lCiAgbXV0YXRlKGZyYWN0X25vbl9hc3NpZ25lZCA9IGlmZWxzZShjYXRlZ29yeSA9PSAibm9uX2Fzc2lnbmVkIiwgZnJhY3Rfbm9uX2Fzc2lnbmVkLCBOQSkpCiAgCmFzc19yZWFkcyAlPiUKICBnZ3Bsb3QoYWVzKHk9cmVhZHMsIHg9cHJvamVjdCwgZmlsbD1jYXRlZ29yeSkpICsKICBnZW9tX2NvbCgpKwogIGdlb21fdGV4dChhZXMobGFiZWw9ZnJhY3Rfbm9uX2Fzc2lnbmVkKSkrCiAgeGxhYigiIikrCiAgeWxhYigiVG90YWwgUmVhZHMiKSsKICBsYWJzKGZpbGw9IkluZGV4IGRlTUwiKQoKCmBgYApQVE8gPiBuZXcgPiBvbGQKYXNzaWduZWQgZnJhY3Rpb25zIHNhbWUKCiMjIEFzc2lnbmVkIGJhcmNvZGVzCmBgYHtyfQojIGdldCBkYXRhCnNldHdkKCIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZGF0YS9GQzIwMjRfMDVfMDJfUG9QOTZfQkEvMDNfelVNSXMvIikKZmlsZV9wYXRoczEgPC0gbGlzdC5maWxlcyhwYXR0ZXJuID0gImtlcHRfYmFyY29kZXNfYmlubmVkXFwudHh0JCIsIHJlY3Vyc2l2ZT1UKQpmaWxlX3BhdGhzMQoKCgpucmVhZHMgPC0gZGF0YS5mcmFtZShzYW1wbGUgPSBOQSwKICAgICAgICAgICAgICAgICAgICAgcmVhZHMgPSBOQSkKCm5yZWFkc19iYXJjb2RlcyA8LSBkYXRhLmZyYW1lKHhjID0gY2hhcmFjdGVyKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsaW5kZXggPSBpbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2plY3Q9Y2hhcmFjdGVyKCkpCgpmb3IgKGkgaW4gMTpsZW5ndGgoZmlsZV9wYXRoczEpKXsKICAKICBiYXJjb2RlcyA8LSByZWFkLmNzdihmaWxlX3BhdGhzMVtpXSkKICBiYXJjb2RlcyRwcm9qZWN0IDwtIG5hbWVzW2ldCiAgbnJlYWRzW2ksMV0gPC0gbmFtZXNbaV0KICBucmVhZHNbaSwyXSA8LSBzdW0oYmFyY29kZXMkbikKICBucmVhZHNfYmFyY29kZXMgPC0gcmJpbmQobnJlYWRzX2JhcmNvZGVzLCBiYXJjb2RlcykKICAKCn0KbnJlYWRzX2JhcmNvZGVzIDwtIG5yZWFkc19iYXJjb2RlcyAlPiUKICBncm91cF9ieShwcm9qZWN0KSAlPiUKICBtdXRhdGUoc3VtID0gc3VtKG4pKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKGFjcm9zcyhwcm9qZWN0LCBmYWN0b3IsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBwbG90IG92ZXJhbGwgcmVhZHMgLyBzYW1wbGUKZ2dwbG90KGRhdGE9bnJlYWRzLCBhZXMoeT1yZWFkcywgeD1zYW1wbGUsIGZpbGw9cHJvamVjdCkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX21pbmltYWwoKSsKICB5bGFiKCJSZWFkcyB3aXRoIGFzc2lnbmVkIEJhcmNvZGUiKSsKICB4bGFiKCIiKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIHBsb3QgaW4gYSBkaWZmZXJlbnQgbGF5b3V0CmdncGxvdChkYXRhPW5yZWFkc19iYXJjb2RlcywgYWVzKHk9biwgeD1wcm9qZWN0LCBjb2xvcj1wcm9qZWN0KSkrCiAgI2dlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpKwogIGdlb21fcG9pbnQoKSsKICBjb29yZF9mbGlwKCkrCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fY2xfYm9vdCIpKwogIHlsYWIoIlJlYWRzIHdpdGggYXNzaWduZWQgQmFyY29kZSIpKwogIHhsYWIoIiIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCgpgYGAKbW9zdGx5IGRlcGVuZGVkIG9uIG92ZXJhbGwgcmVhZHMKCgojIyBDb21wYXJlIGFzc2lnbmVkIGJhcmNvZGUgdG8gYXNzaWduZWQgaW5kZXgKCmBgYHtyfQpucmVhZHNfYmFyY29kZXMgJT4lCiAgc2VsZWN0KC1jKFhDLCBuLCBjZWxsaW5kZXgpKSAlPiUKICBsZWZ0X2pvaW4oYXNzX3JlYWRzICU+JSBmaWx0ZXIoY2F0ZWdvcnkgPT0gImFzc2lnbmVkIikgJT4lIHNlbGVjdChwcm9qZWN0LCByZWFkcyksIGJ5PSJwcm9qZWN0IikgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBtdXRhdGUoZnJhY3Rpb25fd2l0aF9iYyA9IHN1bSAvIHJlYWRzKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHByb2plY3QsIHkgPSBmcmFjdGlvbl93aXRoX2JjLCBjb2xvcj1wcm9qZWN0KSkrCiAgeGxhYigiIikrCiAgeWxhYigiQXNzaWduZWQgYmFyY29kZSBcbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gXG4gQXNzaWduZWQgaW5kZXgiKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpgYGAKCgoKIyBVbnVzZWQgYmFyY29kZWQKc2FtZSBhcyBhYm92ZQpgYGB7cn0KIyBkZl9hbGwgPC0gZGF0YS5mcmFtZSh0cnVlX0JDPWxvZ2ljYWwoKSwKIyAgICAgICAgICAgICAgICAgICAgICBuPWludGVnZXIoKSwKIyAgICAgICAgICAgICAgICAgICAgICBmPW51bWVyaWMoKSwKIyAgICAgICAgICAgICAgICAgICAgICBwcm9qZWN0PWNoYXJhY3RlcigpLAojICAgICAgICAgICAgICAgICAgICAgIFBTPWNoYXJhY3RlcigpLAojICAgICAgICAgICAgICAgICAgICAgIHNlcV9hZGFwdGVycz1jaGFyYWN0ZXIoKSkKIyAKIyBzYW1wbGVzIDwtIG5hbWVzCiMgZm9yIChpIGluIHNhbXBsZXMpewojICAgc2VxPWFzLmRhdGEuZnJhbWUocmVhZEZhc3RxKGRpclBhdGggPSAiL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2RhdGEvRkMyMDI0XzA1XzAxX1BvUDk2X1F1YW50aS8wMl90cmltbWluZyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9IHBhc3RlMCgibGFuZTFfcHJpbWVzZXFfUG9QOTZfUXVhbnRpXyIsIGksICJfcjFfdHJpbW1lZC5mcS5neiIpKUBzcmVhZCkKIyAgIGNvbG5hbWVzKHNlcSk9Yygic2VxIikKIyAgIHNlcT1zZXEgJT4lCiMgICAgIG11dGF0ZShzZXE9YXMuY2hhcmFjdGVyKHNlcSkpICU+JQojICAgICBtdXRhdGUoQkM9c3Vic3RyKHNlcSwxLDEyKSkKIyAgIAojICAgQkNzIDwtIGMocmVhZC5jc3YoZmlsZT1wYXN0ZTAoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMV9Qb1A5Nl9RdWFudGkvMDNfelVNSXMvIiwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpLCAiL3pVTUlzX291dHB1dC9PbGROZXdQVE9fIiwgaSwgIi5CQ2Jpbm5pbmcudHh0IiksIAojICAgICAgICAgICAgICAgICAgICAgc2VwPSIsIilbLDFdLAojICAgICAgICAgICAgcmVhZC5jc3YoZmlsZT1wYXN0ZTAoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMV9Qb1A5Nl9RdWFudGkvMDNfelVNSXMvIiwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpLCAiL3pVTUlzX291dHB1dC9PbGROZXdQVE9fIiwgaSwgImtlcHRfYmFyY29kZXMudHh0IiksCiMgICAgICAgICAgICAgICAgICAgICBzZXA9IiwiKVssMV0pCiMgICAKIyAgIGRmIDwtIHNlcSAlPiUgZHBseXI6OmNvdW50KEJDKSAlPiUgYXJyYW5nZSgtbikgJT4lCiMgICAgIG11dGF0ZSh0cnVlX0JDID0gY2FzZV93aGVuKEJDICVpbiUgQkNzIH4gVFJVRSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVCB+IEZBTFNFKSkgJT4lCiMgICAgIGRwbHlyOjpjb3VudCh0cnVlX0JDLCB3dD1uKSAlPiUKIyAgICAgbXV0YXRlKGY9bi9zdW0obikpICU+JQojICAgICBtdXRhdGUocHJvamVjdD1pKQojICAgCiMgICBkZl9hbGwgPC0gcmJpbmQoZGZfYWxsLCBkZikKIyB9CiMgCiMgCiMgZGZfYWxsIDwtIGRmX2FsbCAlPiUKIyAgIG11dGF0ZShwcm9qZWN0ID0gZmFjdG9yKHByb2plY3QsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkKIyAKIyBnZ3Bsb3QoZGF0YT1kZl9hbGwgJT4lIGZpbHRlcih0cnVlX0JDPT1GQUxTRSksIGFlcyh5PWYsIHg9cHJvamVjdCkpKwojICAgZ2VvbV9jb2woKSsKIyAgIHlsYWIoIkFzc2kgXG4gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFxuIEFzc2lnbmVkIGluZGV4IikrCiMgICB4bGFiKCIiKQojIAojIAojICMgcGxvdCB1bnVzZWQgYmFyY29kZSByZWFkcwojICMgLT4gbW9zdGx5IGFmZmVjdGVkIGJ5IHJlYWQgbm8KIyBnZ3Bsb3QoZGF0YT1kZl9hbGwgJT4lIGZpbHRlcih0cnVlX0JDPT1GQUxTRSksIGFlcyh5PW4sIHg9cHJvamVjdCkpKwojICAgZ2VvbV9jb2woKSsKIyAgIHlsYWIoIlJlYWRzIikrCiMgICB4bGFiKCIiKQpgYGAKCiMgQ29tcGxleGl0eQpgYGB7ciwgZXZhbD1GQUxTRX0Kc291cmNlKCIvZGF0YS9ob21lL2ZlbGl4L0NvbXBsZXhpdHkuUiIpCgpzYW1wbGVzIDwtIG5hbWVzCgpzZXREVHRocmVhZHModGhyZWFkcz0zMCkKCmNvbXBsZXhpdHkgPC0gZGF0YS5mcmFtZShSRz1jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgIG49aW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgcHJvamVjdD1jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxpY2F0ZT1pbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICBkc19sZXZlbD1pbnRlZ2VyKCkpCgojIHJlYWRzOiA0IDQ5MCAyNzYgLSA1IDk1MiAwMDIKZm9yIChrIGluIGMoMTAwMDAwMDAsIDEwMDAwMDApKXsKICBwcmludChrKQogIGZvciAoaSBpbiBzYW1wbGVzKXsKICAgIHByaW50KHBhc3RlMCgic2FtcGxlICIsIGkpKQogICAgZm9yIChqIGluIDE6Myl7CiAgICAgIHByaW50KHBhc3RlMCgicmVwICIsIGosICIgb2YgMTAiKSkKICAgICAgaW5wdXRCQU0gPSBwYXN0ZTAoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS8wM196VU1Jcy8iLAogICAgICAgICAgICAgICAgICAgICAgICBpLAogICAgICAgICAgICAgICAgICAgICAgICAiL09sZE5ld1BUT18iLAogICAgICAgICAgICAgICAgICAgICAgICBpLAogICAgICAgICAgICAgICAgICAgICAgICAiLmZpbHRlcmVkLkFsaWduZWQuR2VuZVRhZ2dlZC5zb3J0ZWQuYmFtIgogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgYmNjb3VudCA9IHBhc3RlMCgiL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2RhdGEvRkMyMDI0XzA1XzAyX1BvUDk2X0JBLzAzX3pVTUlzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBpLAogICAgICAgICAgICAgICAgICAgICAgICAgIi96VU1Jc19vdXRwdXQvT2xkTmV3UFRPXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICBpLAogICAgICAgICAgICAgICAgICAgICAgICAgImtlcHRfYmFyY29kZXNfYmlubmVkLnR4dCIKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgYmNjb3VudCA8LSBmcmVhZChiY2NvdW50KQogICAgICBiY2NvdW50PC1zcGxpdFJHKGJjY291bnQ9YmNjb3VudCwgbWVtPSA1MCwgaGFtZGlzdCA9IDApCiAgICAgIHJlYWRzIDwtIHJlYWRzMmdlbmVzX25ld19kcyhmZWF0ZmlsZSA9IGlucHV0QkFNLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmNjb3VudCAgPSBiY2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5leCAgICAgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2h1bmsgICAgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29yZXMgICAgPSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvd25zYW1wbGluZyA9IGspCiAgICAgIHByaW50KG5yb3cocmVhZHMpKQogICAgICByZWFkcyA8LSByZWFkcyAlPiUgZmlsdGVyKCFpcy5uYShHRSkpICU+JSBkcGx5cjo6c2VsZWN0KC1VQiwgLWZ0eXBlKSAlPiUgZGlzdGluY3QoKSAlPiUgZHBseXI6OmNvdW50KFJHKSAlPiUgCiAgICAgICAgbXV0YXRlKHByb2plY3Q9aSkgJT4lCiAgICAgICAgbXV0YXRlKHJlcGxpY2F0ZT1qKSAlPiUKICAgICAgICBtdXRhdGUoZHNfbGV2ZWw9aykKICAgICAgY29tcGxleGl0eSA8LSByYmluZChjb21wbGV4aXR5LCByZWFkcykKICAgICAgcm0ocmVhZHMpCiAgICB9CiAgfQp9CnNhdmVSRFMoY29tcGxleGl0eSwgIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS9jb21wbGV4aXR5LnJkcyIpCmBgYAoKYGBge3J9CmNvbXBsZXhpdHkgPC0gcmVhZFJEUygiL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2RhdGEvRkMyMDI0XzA1XzAyX1BvUDk2X0JBL2NvbXBsZXhpdHkucmRzIikKY29tcGxleGl0eTIgPC0gY29tcGxleGl0eSAlPiUKICBtdXRhdGUoYWNyb3NzKHByb2plY3QsIGZhY3RvciwgbGV2ZWxzPWMoIm9sZCIsICJuZXciLCAiUFRPIikpKSAlPiUKICBtdXRhdGUoZHNfbGV2ZWwgPSBpZmVsc2UoZHNfbGV2ZWwgPT0gMTAwMDAwMDAsICJubyBkb3duc2FtcGxpbmciLCBkc19sZXZlbCkpICU+JQogIG11dGF0ZShkc19sZXZlbCA9IGZhY3Rvcihkc19sZXZlbCwgbGV2ZWxzID0gYygibm8gZG93bnNhbXBsaW5nIiwgIjFlKzA2IikpKQoKYSA8LSBnZ3Bsb3QoY29tcGxleGl0eTIsIGFlcyh5PW4sIHg9cHJvamVjdCwgZmlsbD1wcm9qZWN0KSkrCiAgZ2VvbV92aW9saW4oKSsKICBjb29yZF9mbGlwKCkrCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gIm1lYW5fY2xfYm9vdCIsIGdlb20gPSAicG9pbnRyYW5nZSIsCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIpKwogIGZhY2V0X2dyaWQofmRzX2xldmVsLCBzY2FsZT0iZnJlZSIpKwogIHhsYWIoIiIpKwogIHlsYWIoIkNvbXBsZXhpdHkgKERldGVjdGVkIEdlbmVzKSIpKwogIHRoZW1lX1B1YmxpY2F0aW9uKCkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQphCmBgYAoKCiMgRGVtdWx0aXBsZXhpbmcKYGBge3J9CiMgbG9hZCBkYXRhCmRlbWwgPC0gcmVhZC5kZWxpbShmaWxlPSIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZGF0YS9GQzIwMjRfMDVfMDJfUG9QOTZfQkEvZGVNTF9zdW1tYXJ5X3RvX3JlYWRfaW4udHh0IikgJT4lCiAgICBkcGx5cjo6cmVuYW1lKCJwcm9qZWN0Ij0iUkciKSAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdChwcm9qZWN0LCAicHJpbWVzZXEiKSkgJT4lCiAgbXV0YXRlKHByb2plY3QgPSBzdHJfZXh0cmFjdChwcm9qZWN0LCAiLi4uJCIpKSAlPiUKICBtdXRhdGUoYWNyb3NzKHByb2plY3QsIGZhY3RvciwgbGV2ZWxzPWMoIm9sZCIsICJuZXciLCAiUFRPIikpKSAlPiUKICBtdXRhdGUodG90YWxfZnJhY3QgPSBhcy5udW1lcmljKHN1YigiJSQiLCAiIiwgdG90YWwuKSkvMTAwKSAlPiUgc2VsZWN0KC10b3RhbC4pICU+JQogIG11dGF0ZShhc3NpZ25lZF9mcmFjdCA9IGFzLm51bWVyaWMoc3ViKCIlJCIsICIiLCBhc3NpZ25lZC4pKS8xMDApICU+JSBzZWxlY3QoLWFzc2lnbmVkLikgJT4lCiAgbXV0YXRlKHVua25vd25fZnJhY3QgPSBhcy5udW1lcmljKHN1YigiJSQiLCAiIiwgdW5rbm93bi4pKS8xMDApICU+JSBzZWxlY3QoLXVua25vd24uKSU+JQogIG11dGF0ZShjb25mbGljdF9mcmFjdCA9IGFzLm51bWVyaWMoc3ViKCIlJCIsICIiLCBjb25mbGljdC4pKS8xMDApICU+JSBzZWxlY3QoLWNvbmZsaWN0LikgJT4lCiAgbXV0YXRlKHdyb25nX2ZyYWN0ID0gYXMubnVtZXJpYyhzdWIoIiUkIiwgIiIsIHdyb25nLikpLzEwMCkgJT4lIHNlbGVjdCgtd3JvbmcuKQoKIyBtYWtlIGl0IGxvbmcKZGVtbF9sb25nIDwtIGRlbWwgJT4lIHNlbGVjdChjKDEsIDc6MTEpKSAlPiUKICBwaXZvdF9sb25nZXIoY29scz0yOjYsIG5hbWVzX3RvPSJjYXRlZ29yeSIsIHZhbHVlc190bz0iZnJhY3Rpb24iKSAlPiUKICBtdXRhdGUoY2F0ZWdvcnk9c3ViKCJfZnJhY3QiLCAiIiwgY2F0ZWdvcnkpKSAlPiUKICBtdXRhdGUoZnJhY3Rpb24gPSBhcy5udW1lcmljKGZyYWN0aW9uKSklPiUKICBtdXRhdGUoY2F0ZWdvcnkgPSBmYWN0b3IoY2F0ZWdvcnksIGxldmVscz1jKCJ0b3RhbCIsICJjb25mbGljdCIsICJ3cm9uZyIsICJ1bmtub3duIiwgImFzc2lnbmVkIikpKQoKIyB3aGF0IGRvZXMgd2hhdCBtZWFuOgojICJ1bmtub3duIjogSXQncyBtb3JlIGxpa2VseSB0aGF0IHlvdSBiZWxvbmcgdG8gdGhhdCBzYW1wbGUgdGhhbiBhbnkgb3RoZXIgYnV0IHRoYXQgcHJvYmFiaWxpdHkgaXMgbG93CiMgImNvbmZsaWN0IjogSXQncyBtb3JlIGxpa2VseSB0aGF0IHlvdSBiZWxvbmcgdG8gdGhhdCBzYW1wbGUgdGhhbiBhbnkgb3RoZXIgYnV0IHRoZSBwcm9iYWJpbGl0eSB0aGF0IHlvdSBiZWxvbmcgdG8gYW5vdGhlciBzYW1wbGUgaXMgYWxtb3N0IGVxdWFsbHkgbGlrZWx5CiMgIndyb25nIjogSXQncyBtb3JlIGxpa2VseSB0aGF0IHlvdSBiZWxvbmcgdG8gdGhhdCBzYW1wbGUgdGhhbiBhbnkgb3RoZXIgYnV0IGl0IHNlZW1zIHlvdXIgaW5kaWNlcyBhcmUgbWlzcGFpcmVkCgojIHBsb3QKcGxvdCA8LSBnZ3Bsb3QoZGVtbF9sb25nICU+JSBmaWx0ZXIoY2F0ZWdvcnkgIT0gInRvdGFsIiksIGFlcyh4ID0gcHJvamVjdCwgeT1mcmFjdGlvbiwgZmlsbCA9IGNhdGVnb3J5KSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKyAKICAjc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9YygiRDIiLCAiRDAuNSIsICJEMSIpKSsKICB4bGFiKCIiKSsKICB5bGFiKCJGcmFjdGlvbiBvZiB0b3RhbCByZWFkcyIpKwogIGxhYnMoZmlsbD0iRGVtdWx0aXBsZXhpbmcgY2F0ZWdvcnkiKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJhc3NpZ25lZCIgPSAibGlnaHRncmVlbiIsICJ1bmtub3duIiA9ICJncmV5NzAiLCAid3JvbmciID0gImdyZXk1MCIsICJjb25mbGljdCIgPSAiZ3JleTMwIikpKwogICMgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAuNSwgMSkpKwogIGNvb3JkX2ZsaXAoKSsKICB0aGVtZV9QdWJsaWNhdGlvbigpCgpwbG90CnBsb3RfY3V0IDwtIHBsb3QrCiAgY29vcmRfZmxpcCh5bGltID0gYygwLjk1LCAxKSkKCiMgZ2dzYXZlKHBsb3QsIGZpbGVuYW1lPSIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZmlndXJlcy9maWdfMV9kZW11bHRpcGxleGluZy5wZGYiLCBoZWlnaHQ9Mi41LCB3aWR0aD0xMikKIyBnZ3NhdmUocGxvdF9jdXQsIGZpbGVuYW1lPSIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZmlndXJlcy9maWdfMV9kZW11bHRpcGxleGluZ19jdXQucGRmIiwgaGVpZ2h0PTIsIHdpZHRoPTYpCmBgYAoKIyBvdmVycmVwcmVzZW50ZWQgc2VxcyBpbiByZWFkIDIKYGBge3J9CmNvbWJpbmVkX2RmIDwtIGRhdGEuZnJhbWUoKQoKZm9yIChuIGluIG5hbWVzKSB7CiAgdGVtcCA8LSByZWFkLmRlbGltKAogICAgcGFzdGUwKCIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZGF0YS9GQzIwMjRfMDVfMDJfUG9QOTZfQkEvb3ZlcnJlcHJlc2VudGVkLyIsIAogICAgICAgICAgIG4sIAogICAgICAgICAgICIub3V0LnR4dCIpKSAlPiUKICAgIG11dGF0ZShjb25kaXRpb249bikKICAKICBjb21iaW5lZF9kZiA8LSBiaW5kX3Jvd3MoY29tYmluZWRfZGYsIHRlbXApJT4lCiAgbXV0YXRlKGFjcm9zcyhjb25kaXRpb24sIGZhY3RvciwgbGV2ZWxzPWMoIm9sZCIsICJuZXciLCAiUFRPIikpKQp9Cgpjb21iaW5lZF9kZiAlPiUKICBnZ3Bsb3QoYWVzKHg9U2VxdWVuY2UsIHk9UGVyY2VudGFnZSkpKwogIGdlb21fY29sKHNpemU9MikrCiAgZmFjZXRfZ3JpZChjb25kaXRpb25+LikrCiAgY29vcmRfZmxpcCgpCmBgYApib3RoIHNlcXVlbmNlcyBnZXQgcmVkdWNlZCwgZXNwLiBHR0dHRy4uLgoKCiMgRmlsdGVyaW5nCmdyZXAgLUUgIlRvdGFsIHJlYWQgcGFpcnMuKnxSZWFkIDIgd2l0aC4qfFBhaXJzIHRoYXQuKiIgIiRpbnB1dF9maWxlIgpgYGB7cn0KdHJpbV9kZiA8LSBkYXRhLmZyYW1lKCkKCmZvciAobiBpbiBuYW1lcykgewogIHRlbXAgPC0gcmVhZF9kZWxpbShwYXN0ZTAoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS8wMl90cmltbWluZy8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIi50eHQiKSwgCiAgICAgICAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IGMoImNhdGVnb3J5IiwgInJlYWRzIikpICU+JSAKICAgIG11dGF0ZShyZWFkcyA9IHN0cl9yZXBsYWNlX2FsbChyZWFkcywgIiAiLCAiIikpICU+JSAKICAgIG11dGF0ZShyZWFkcyA9IHN0cl9yZXBsYWNlX2FsbChyZWFkcywgIiwiLCAiIikpICU+JSAKICAgIG11dGF0ZShyZWFkcyA9IHN0cl9yZXBsYWNlX2FsbChyZWFkcywgIiVcXCkiLCAiIikpICU+JSAKICAgIHNlcGFyYXRlX3dpZGVyX2RlbGltKHJlYWRzLCBkZWxpbSA9ICIoIiwgbmFtZXMgPSBjKCJyZWFkcyIsICJwZXJjZW50YWdlIiksIHRvb19mZXcgPSAiYWxpZ25fc3RhcnQiKSAlPiUKICAgIG11dGF0ZShjb25kaXRpb249bikKICAKICB0cmltX2RmIDwtIGJpbmRfcm93cyh0cmltX2RmLCB0ZW1wKQp9CnRyaW1fZGYgPC0gdHJpbV9kZiAlPiUKICAgIG11dGF0ZShhY3Jvc3MoY29uZGl0aW9uLCBmYWN0b3IsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkgJT4lCiAgICBtdXRhdGUocmVhZHMgPSBhcy5udW1lcmljKHJlYWRzKSwgcGVyY2VudGFnZSA9IGFzLm51bWVyaWMocGVyY2VudGFnZSkpCgoKdHJpbV9kZiAlPiUKICBmaWx0ZXIoY2F0ZWdvcnkgIT0gIlRvdGFsIHJlYWQgcGFpcnMgcHJvY2Vzc2VkIikgJT4lCiAgZ2dwbG90KGFlcyh5PXBlcmNlbnRhZ2UsIHg9Y2F0ZWdvcnkpKSArCiAgZ2VvbV9jb2woc2l6ZT0yKSsKICBmYWNldF9ncmlkKGNvbmRpdGlvbn4uKSsKICBjb29yZF9mbGlwKCkKCgpgYGAKcmVkdWN0aW9uIGZyb20gb2xkIHRvIG5ldyBpbiBib3RoCnNsaWdodCBpbmNyZWFzZSBmcm9tIG5ldyB0byBQVE8gaW4gYm90aAoKCiMgQkMgYmlubmluZwpgYGB7cn0KYmluX2RmIDwtIGRhdGEuZnJhbWUoKQoKZm9yIChuYSBpbiBuYW1lcykgewogIHRlbXAgPC0gcmVhZF9kZWxpbShwYXN0ZTAoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS8wM196VU1Jcy8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvelVNSXNfb3V0cHV0L09sZE5ld1BUT18iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIuQkNiaW5uaW5nLnR4dCIpKSAlPiUgCiAgICBtdXRhdGUocHJvamVjdD1uYSkKICAKICBiaW5fZGYgPC0gYmluZF9yb3dzKGJpbl9kZiwgdGVtcCkKfQoKYmluX2RmIDwtIGJpbl9kZiAlPiUKICAgIG11dGF0ZShhY3Jvc3MocHJvamVjdCwgZmFjdG9yLCBsZXZlbHM9Yygib2xkIiwgIm5ldyIsICJQVE8iKSkpCgoKYmluX2RmICU+JQogIGdyb3VwX2J5KHByb2plY3QsIGhhbW1pbmcpICU+JQogIHJlZnJhbWUodG90YWw9c3VtKG4pKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbChhZXMoeD1wcm9qZWN0LCB5PXRvdGFsLCBmaWxsPXByb2plY3QpKSsKICBmYWNldF9ncmlkKC5+aGFtbWluZykrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICBnZ3RpdGxlKCJCYXJjb2RlcyBiaW5uZWQgYnkgSGFtbWluZyBEaXN0YW5jZSIpKwogIHlsYWIoIlJlYWRzIikrCiAgeGxhYigiIikKCiMgYXMgZnJhY3Rpb24gb2YgdG90YWwgcmVhZHMKQkNfYmlubmluZ19IRF9wbG90IDwtIGJpbl9kZiAlPiUKICBncm91cF9ieShwcm9qZWN0LCBoYW1taW5nKSAlPiUKICByZWZyYW1lKHRvdGFsPXN1bShuKSkgJT4lCiAgbGVmdF9qb2luKG5yZWFkcywgYnk9YygicHJvamVjdCIgPSAic2FtcGxlIikpICU+JQogIG11dGF0ZShiaW5fZnJhY3Rpb24gPSB0b3RhbC9yZWFkcykgJT4lCiAgICBtdXRhdGUoYWNyb3NzKHByb2plY3QsIGZhY3RvciwgbGV2ZWxzPWMoIm9sZCIsICJuZXciLCAiUFRPIikpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbChhZXMoeD1wcm9qZWN0LCB5PWJpbl9mcmFjdGlvbiwgZmlsbD1wcm9qZWN0KSwgcG9zaXRpb249ImRvZGdlIikrCiAgICBmYWNldF9ncmlkKC5+aGFtbWluZykrCiAgIyBmYWNldF9ncmlkKGhhbW1pbmd+Liwgc2NhbGVzPSJmcmVlIikrCiAgZ2d0aXRsZSgiSGFtbWluZyBEaXN0YW5jZSIpKwogIHlsYWIoZXhwcmVzc2lvbihmcmFjKCdSZWFkcyBiaW5uZWQnLCAnQXNzaWduZWQgYmFyY29kZSByZWFkcycpKSkrCiAgeGxhYigiIikrCiAgdGhlbWVfUHVibGljYXRpb24oKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIGdnc2F2ZShwbG90ID0gQkNfYmlubmluZ19IRF9wbG90LCAKIyAgICAgICAgZmlsZW5hbWUgPSAiL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2ZpZ3VyZXMvZmlnX1NfQkNfYmlubmluZy5wZGYiLAojICAgICAgICB3aWR0aD03LAojICAgICAgICBoZWlnaHQ9NikKCiMgcGVyIEJDCkJDX2Jpbm5pbmdfSERfcGxvdApiaW5fZGZfQkMgPC0gYmluX2RmICU+JQogIGdyb3VwX2J5KGhhbW1pbmcsIHRydWVCQywgcHJvamVjdCkgJT4lCiAgcmVmcmFtZShuX2Jpbm5lZCA9IHN1bShuKSkgJT4lCiAgbGVmdF9qb2luKG5yZWFkc19iYXJjb2RlcywgYnk9YygicHJvamVjdCIsICJ0cnVlQkMiID0gIlhDIikpICU+JQogIG11dGF0ZShiaW5fZnJhY3Rpb24gPSBuX2Jpbm5lZC9uKSAlPiUKICAgIG11dGF0ZShhY3Jvc3MocHJvamVjdCwgZmFjdG9yLCBsZXZlbHM9Yygib2xkIiwgIm5ldyIsICJQVE8iKSkpCgpCQ19iaW5uaW5nX0hEX3Bsb3RfMiA8LSBiaW5fZGZfQkMgJT4lCiAgZ2dwbG90KCkrCiAgZ2VvbV9ib3hwbG90KGFlcyh4PXByb2plY3QsIHk9YmluX2ZyYWN0aW9uLCBmaWxsPXByb2plY3QpLCBwb3NpdGlvbj0iZG9kZ2UiKSsKICAjICBmYWNldF9ncmlkKC5+aGFtbWluZykrCiAgIGZhY2V0X2dyaWQoaGFtbWluZ34uLCBzY2FsZXM9ImZyZWUiKSsKICBnZ3RpdGxlKCJIYW1taW5nIERpc3RhbmNlIikrCiAgeWxhYihleHByZXNzaW9uKGZyYWMoJ1JlYWRzIGJpbm5lZCcsICdBc3NpZ25lZCBiYXJjb2RlIHJlYWRzJykpKSsKICB4bGFiKCIiKSsKICB0aGVtZV9QdWJsaWNhdGlvbigpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCkJDX2Jpbm5pbmdfSERfcGxvdF8yCgojIGdnc2F2ZShwbG90ID0gQkNfYmlubmluZ19IRF9wbG90XzIsCiMgICAgICAgIGZpbGVuYW1lID0gIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9maWd1cmVzL2ZpZ19TX0JDX2Jpbm5pbmdfYnlfQkMucGRmIiwKIyAgICAgICAgd2lkdGg9NCwKIyAgICAgICAgaGVpZ2h0PTcpCgojIGRpc3RyaWJ1dGlvbgpiaW5fZGYgJT4lCiAgbGVmdF9qb2luKG5yZWFkc19iYXJjb2RlcyAlPiUgZHBseXI6OnJlbmFtZSgicmVhZHMiID0gIm4iKSwgYnk9YygicHJvamVjdCIsICJ0cnVlQkMiID0gIlhDIikpICU+JQogIGdyb3VwX2J5KGhhbW1pbmcsIHRydWVCQywgcHJvamVjdCkgJT4lCiAgc3VtbWFyaXNlKG5fZnJhY3Rpb24gPSBzdW0obikgLyBzdW0ocmVhZHMpKSAlPiUKICBtdXRhdGUoYWNyb3NzKHByb2plY3QsIGZhY3RvciwgbGV2ZWxzPWMoIm9sZCIsICJuZXciLCAiUFRPIikpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2NvbChhZXMoeT1uX2ZyYWN0aW9uLCB4PXJlb3JkZXIodHJ1ZUJDLCAtbl9mcmFjdGlvbiksIGZpbGw9cHJvamVjdCkpKwogIGZhY2V0X2dyaWQoaGFtbWluZ35wcm9qZWN0LCBzY2FsZT0iZnJlZSIpKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpKwogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiByZWFkcyBwZXIgYmFyY29kZSIpKwogIHlsYWIoZXhwcmVzc2lvbihmcmFjKCdSZWFkcyBiaW5uZWQnLCAnQXNzaWduZWQgYmFyY29kZSByZWFkcycpKSkrCiAgeGxhYigiQmFyY29kZXMiKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpiaW5fZGYgJT4lCiAgbGVmdF9qb2luKG5yZWFkc19iYXJjb2RlcyAlPiUgZHBseXI6OnJlbmFtZSgicmVhZHMiID0gIm4iKSwgYnk9YygicHJvamVjdCIsICJ0cnVlQkMiID0gIlhDIikpICU+JQogIGdyb3VwX2J5KGhhbW1pbmcsIHRydWVCQywgcHJvamVjdCkgJT4lCiAgc3VtbWFyaXNlKG5fYWJzb2x1dGUgPSBzdW0obikpICU+JQogIG11dGF0ZShhY3Jvc3MocHJvamVjdCwgZmFjdG9yLCBsZXZlbHM9Yygib2xkIiwgIm5ldyIsICJQVE8iKSkpICU+JQogIGdncGxvdCgpKwogIGdlb21fY29sKGFlcyh5PW5fYWJzb2x1dGUsIHg9cmVvcmRlcih0cnVlQkMsIC1uX2Fic29sdXRlKSwgZmlsbD1wcm9qZWN0KSkrCiAgZmFjZXRfZ3JpZChoYW1taW5nfnByb2plY3QsIHNjYWxlPSJmcmVlIikrIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkrCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIHJlYWRzIHBlciBiYXJjb2RlIikrCiAgeWxhYignUmVhZHMgYmlubmVkJykrCiAgeGxhYigiQmFyY29kZXMiKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAojIFJlYWQgLyBVTUkgY291bnQgZGF0YQpgYGB7cn0KIyByZWFkIGluOgpyZWFkX3VtaSA8LSBkYXRhLmZyYW1lKCkKZm9yIChpIGluIG5hbWVzKSB7CiAgZ2VuZV9jb3VudHMgPC0gcmVhZF9yZHMoCiAgICBmaWxlPXBhc3RlMCgiL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2RhdGEvRkMyMDI0XzA1XzAyX1BvUDk2X0JBLzAzX3pVTUlzLyIsIAogICAgICAgICAgICAgICAgaSwgCiAgICAgICAgICAgICAgICAiL3pVTUlzX291dHB1dC9zdGF0cy9PbGROZXdQVE9fIiwgCiAgICAgICAgICAgICAgICBpLCAKICAgICAgICAgICAgICAgICIuYmMuUkVBRGNvdW50cy5yZHMiKQogICAgKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUocmVhZF9jb3VudCA9IE4sIFNhbXBsZUlEID0gUkcpCiAgdW1pX2NvdW50cyA8LSByZWFkX2RlbGltKAogICAgZmlsZT1wYXN0ZTAoIi9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9kYXRhL0ZDMjAyNF8wNV8wMl9Qb1A5Nl9CQS8wM196VU1Jcy8iLCAKICAgICAgICAgICAgICAgIGksIAogICAgICAgICAgICAgICAgIi96VU1Jc19vdXRwdXQvc3RhdHMvT2xkTmV3UFRPXyIsIAogICAgICAgICAgICAgICAgaSwgCiAgICAgICAgICAgICAgICAiLlVNSWNvdW50cy50eHQiKQogICAgKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodW1pX2NvdW50ID0gQ291bnQpCiAgcmVhZF91bWkgPC0gcmJpbmQocmVhZF91bWksIAogICAgICAgICAgICAgICAgICAgIGZ1bGxfam9pbihnZW5lX2NvdW50cywgdW1pX2NvdW50cywgYnk9YygiU2FtcGxlSUQiLCAidHlwZSIpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShwcm9qZWN0ID0gaSkKICAgICAgICAgICAgICAgICAgICApCn0KcmVhZF91bWlfcmF3IDwtIHJlYWRfdW1pICU+JQogIG11dGF0ZShwcm9qZWN0ID0gZmFjdG9yKHByb2plY3QsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkKIyBwcm9jZXNzCnJlYWRfdW1pIDwtIHJlYWRfdW1pICU+JQogIGZpbHRlcih0eXBlICVpbiUgYygiRXhvbiIsICJJbnRyb24iKSAmIAogICAgICAgICAgIFNhbXBsZUlEICE9ICJiYWQiKSAlPiUKICBtdXRhdGUodW1pX2ZyYWN0aW9uID0gdW1pX2NvdW50L3JlYWRfY291bnQpICU+JQogIG11dGF0ZShwcm9qZWN0ID0gZmFjdG9yKHByb2plY3QsIGxldmVscz1jKCJvbGQiLCAibmV3IiwgIlBUTyIpKSkKCnJlYWRfdW1pICU+JQogIGdncGxvdChhZXMoeT11bWlfZnJhY3Rpb24sIHg9cHJvamVjdCwgY29sb3I9cHJvamVjdCkpKwogIGdlb21fcXVhc2lyYW5kb20oKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwKICAgICAgICAgICAgICAgZ2VvbSA9ICJjcm9zc2JhciIpKwogIGZhY2V0X2dyaWQofnR5cGUpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrCiAgeWxhYihleHByZXNzaW9uKGZyYWMoVU1JcywgUmVhZHMpKSkKICAKcmVhZF91bWlfc3VtbWFyeSA8LSByZWFkX3VtaSAlPiUKICBncm91cF9ieShwcm9qZWN0KSAlPiUKICBzdW1tYXJpc2UoVU1JID0gc3VtKHVtaV9jb3VudCkpCmBgYAoKCgpgYGB7ciBzZXR1cDIsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkocGxvdGx5KQpgYGAKCiMgRnVubmVsCmBgYHtyfQpyZWFkX2Z1bm5lbCAgPC0KICBhc3NfcmVhZHMgJT4lCiAgZmlsdGVyKGNhdGVnb3J5ID09ICJhc3NpZ25lZCIpICU+JQogIHNlbGVjdChwcm9qZWN0LCB0b3RhbCwgImluZGV4X2Fzc2lnbmVkIiA9ICJyZWFkcyIpICU+JQogIGZ1bGxfam9pbigKICAgIHRyaW1fZGYgJT4lCiAgICAgIHNlbGVjdCgtcGVyY2VudGFnZSkgJT4lCiAgICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjYXRlZ29yeSwgdmFsdWVzX2Zyb20gPSByZWFkcykgJT4lCiAgICAgIGdyb3VwX2J5KGNvbmRpdGlvbikgJT4lCiAgICAgIG11dGF0ZSh0cmltbWVkID0gYFRvdGFsIHJlYWQgcGFpcnMgcHJvY2Vzc2VkYCAtIGBQYWlycyB0aGF0IHdlcmUgdG9vIHNob3J0YCkgJT4lCiAgICAgIHNlbGVjdChwcm9qZWN0ID0gY29uZGl0aW9uLCB0cmltbWVkKSwgYnk9InByb2plY3QiKSAlPiUKICBwaXZvdF9sb25nZXIoY29scz0tMSwgbmFtZXNfdG8gPSAic3RlcCIsIHZhbHVlc190byA9ICJyZWFkcyIpICU+JQogIGJpbmRfcm93cygKICAgIG5yZWFkc19iYXJjb2RlcyAlPiUgc2VsZWN0KEJDID0gWEMsIGJhcmNvZGVfYXNzaWduZWQgPSBuLCBwcm9qZWN0KSAlPiUKICAgICAgZnVsbF9qb2luKHJwY19hbGxfc21wbCAlPiUgCiAgICAgICAgICAgICAgICAgIHNlbGVjdChCQyA9IFJHLCBOLCB0eXBlLCBwcm9qZWN0KSAlPiUKICAgICAgICAgICAgICAgICAgdW5ncm91cCgpICU+JSAKICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkocHJvamVjdCwgQkMpICU+JSAKICAgICAgICAgICAgICAgICAgZmlsdGVyKHR5cGUgJWluJSBjKCJJbnRyb24iLCAiRXhvbiIpKSAlPiUgCiAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShpbmV4PXN1bShOKSksCiAgICAgICAgICAgICAgICBieT1jKCJwcm9qZWN0IiwgIkJDIikpICU+JQogICAgICBmdWxsX2pvaW4ocmVhZF91bWkgJT4lIAogICAgICAgICAgICAgICAgICBzZWxlY3QoQkMgPSBTYW1wbGVJRCwgVU1JPSB1bWlfY291bnQsIHR5cGUsIHByb2plY3QpICU+JQogICAgICAgICAgICAgICAgICBncm91cF9ieShwcm9qZWN0LCBCQykgJT4lCiAgICAgICAgICAgICAgICAgIGZpbHRlcih0eXBlICVpbiUgYygiSW50cm9uIiwgIkV4b24iKSkgJT4lIAogICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoVU1JPXN1bShVTUkpKSwKICAgICAgICAgICAgICAgIGJ5PWMoInByb2plY3QiLCAiQkMiKSkgJT4lCiAgICAgIHBpdm90X2xvbmdlcihjb2xzPS1jKCJCQyIsICJwcm9qZWN0IiksIG5hbWVzX3RvID0gInN0ZXAiLCB2YWx1ZXNfdG8gPSAicmVhZHMiKSkgJT4lCiAgZmlsdGVyKHByb2plY3QgIT0gIm5ldyIpICU+JQogIG11dGF0ZShwcm9qZWN0ID0gaWZlbHNlKHByb2plY3QgPT0gIlBUTyIsICJwcmltZS1zZXEtb3B0aSIsICJwcmltZS1zZXEiKSkgJT4lCiAgbXV0YXRlKHN0ZXAgPSBmYWN0b3Ioc3RlcCwgbGV2ZWxzID0gYygidG90YWwiLCAiaW5kZXhfYXNzaWduZWQiLCAidHJpbW1lZCIsICJiYXJjb2RlX2Fzc2lnbmVkIiwgImluZXgiLCAiVU1JIikpKSAlPiUKICBtdXRhdGUobWF4ID0gbWF4KHJlYWRzKSkgJT4lCiAgZ3JvdXBfYnkocHJvamVjdCkgJT4lCiAgbXV0YXRlKG1heF9wcm9qZWN0ID0gbWF4KHJlYWRzKSkgJT4lCiAgICBncm91cF9ieShwcm9qZWN0LCBCQykgJT4lCiAgbXV0YXRlKGZyYWN0aW9ucyA9IHJlYWRzL21heF9wcm9qZWN0KSAlPiUKICBtdXRhdGUoZnJhY3Rpb25zID0gaWZlbHNlKCFpcy5uYShCQyksIGZyYWN0aW9ucyoxMSwgZnJhY3Rpb25zKSkgJT4lCiAgI211dGF0ZShmcmFjdGlvbnMgPSBpZmVsc2UoaXMubmEoZnJhY3Rpb25zKSwgMSwgZnJhY3Rpb25zKSkgJT4lCiAgbXV0YXRlKGZyYWN0aW9uc19yZWxfbWF4ID0gcmVhZHMvbWF4KSAlPiUKICBtdXRhdGUoZnJhY3Rpb25zX3JlbF9tYXggPSBpZmVsc2UoIWlzLm5hKEJDKSwgZnJhY3Rpb25zX3JlbF9tYXgqMTEsIGZyYWN0aW9uc19yZWxfbWF4KSkKCnJlYWRfZnVubmVsX2F2ZyAgPC0KICBhc3NfcmVhZHMgJT4lCiAgZmlsdGVyKGNhdGVnb3J5ID09ICJhc3NpZ25lZCIpICU+JQogIHNlbGVjdChwcm9qZWN0LCB0b3RhbCwgImluZGV4X2Fzc2lnbmVkIiA9ICJyZWFkcyIpICU+JQogIGxlZnRfam9pbigKICAgIHRyaW1fZGYgJT4lCiAgICAgIHNlbGVjdCgtcGVyY2VudGFnZSkgJT4lCiAgICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjYXRlZ29yeSwgdmFsdWVzX2Zyb20gPSByZWFkcykgJT4lCiAgICAgIGdyb3VwX2J5KGNvbmRpdGlvbikgJT4lCiAgICAgIG11dGF0ZSh0cmltbWVkID0gYFRvdGFsIHJlYWQgcGFpcnMgcHJvY2Vzc2VkYCAtIGBQYWlycyB0aGF0IHdlcmUgdG9vIHNob3J0YCkgJT4lCiAgICAgIHNlbGVjdChwcm9qZWN0ID0gY29uZGl0aW9uLCB0cmltbWVkKSwgYnk9InByb2plY3QiKSAlPiUKICBsZWZ0X2pvaW4obnJlYWRzLCBieT1jKCJwcm9qZWN0IiA9ICJzYW1wbGUiKSkgJT4lCiAgZHBseXI6OnJlbmFtZSgiYmFyY29kZV9hc3NpZ25lZCIgPSAicmVhZHMiKSAlPiUKICBsZWZ0X2pvaW4ocnBjX2FsbF9zbXBsICU+JSAKICAgICAgICAgICAgICB1bmdyb3VwKCkgJT4lIAogICAgICAgICAgICAgIGdyb3VwX2J5KHByb2plY3QpICU+JSAKICAgICAgICAgICAgICBmaWx0ZXIodHlwZSAlaW4lIGMoIkludHJvbiIsICJFeG9uIikpICU+JSAKICAgICAgICAgICAgICBzdW1tYXJpc2UoaW5leD1zdW0oTikpLAogICAgICAgICAgICBieT0icHJvamVjdCIpICU+JQogIGxlZnRfam9pbihyZWFkX3VtaV9zdW1tYXJ5LCBieT0icHJvamVjdCIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzPS0xLCBuYW1lc190byA9ICJzdGVwIiwgdmFsdWVzX3RvID0gInJlYWRzIikgJT4lCiAgZmlsdGVyKHByb2plY3QgIT0gIm5ldyIpICU+JQogIG11dGF0ZShwcm9qZWN0ID0gaWZlbHNlKHByb2plY3QgPT0gIlBUTyIsICJwcmltZS1zZXEtb3B0aSIsICJwcmltZS1zZXEiKSkgJT4lCiAgbXV0YXRlKHN0ZXAgPSBmYWN0b3Ioc3RlcCwgbGV2ZWxzID0gYygidG90YWwiLCAiaW5kZXhfYXNzaWduZWQiLCAidHJpbW1lZCIsICJiYXJjb2RlX2Fzc2lnbmVkIiwgImluZXgiLCAiVU1JIikpKSAlPiUKICBtdXRhdGUobWF4ID0gbWF4KHJlYWRzKSkgJT4lCiAgZ3JvdXBfYnkocHJvamVjdCkgJT4lCiAgbXV0YXRlKG1heF9wcm9qZWN0ID0gbWF4KHJlYWRzKSkgJT4lCiAgbXV0YXRlKGZyYWN0aW9ucyA9IHJlYWRzL21heF9wcm9qZWN0KSAlPiUKICAjbXV0YXRlKGZyYWN0aW9ucyA9IGlmZWxzZShpcy5uYShmcmFjdGlvbnMpLCAxLCBmcmFjdGlvbnMpKSAlPiUKICBtdXRhdGUoZnJhY3Rpb25zX3JlbF9tYXggPSByZWFkcy9tYXgpCgpgYGAKCmBgYHtyfQpyZWFkX2Z1bm5lbCAlPiUgCiAgZ2dwbG90KCkrCiAgZ2VvbV9saW5lKGFlcyh5PXJlYWRzLCB4PXN0ZXAsIGNvbG9yPXByb2plY3QsIGdyb3VwPXByb2plY3QpKQoKcmVhZF9mdW5uZWwgJT4lIAogIGdncGxvdCgpKwogIGdlb21fbGluZShhZXMoeT1mcmFjdGlvbnMsIHg9c3RlcCwgY29sb3I9cHJvamVjdCwgZ3JvdXA9cHJvamVjdCkpKwogIHlsYWIoIkZyYWN0aW9uIG9mIHRvdGFsIikKCnJlYWRfZnVubmVsICU+JSAKICBnZ3Bsb3QoKSsKICBnZW9tX2xpbmUoYWVzKHk9ZnJhY3Rpb25zX3JlbF9tYXgsIHg9c3RlcCwgY29sb3I9cHJvamVjdCwgZ3JvdXA9cHJvamVjdCkpKwogIHlsYWIoIkZyYWN0aW9uIG9mIG1heCB0b3RhbCIpCgoKCgpyZWFkX2Z1bm5lbCAlPiUgIAogIGZpbHRlcihwcm9qZWN0ID09ICJvbGQiKSAlPiUKICBwbG90X2x5KAogICAgdHlwZSA9ICJmdW5uZWwiLAogICAgeSA9IH5zdGVwLAogICAgeCA9IH5yZWFkcywKICAgIHRleHRpbmZvID0gInZhbHVlK3BlcmNlbnQgaW5pdGlhbCIpCgpyZWFkX2Z1bm5lbCAlPiUgIAogIGZpbHRlcihwcm9qZWN0ID09ICJuZXciKSAlPiUKICBwbG90X2x5KAogICAgdHlwZSA9ICJmdW5uZWwiLAogICAgeSA9IH5zdGVwLAogICAgeCA9IH5yZWFkcywKICAgIHRleHRpbmZvID0gInZhbHVlK3BlcmNlbnQgaW5pdGlhbCIpCgpyZWFkX2Z1bm5lbCAlPiUgIAogIGZpbHRlcihwcm9qZWN0ID09ICJQVE8iKSAlPiUKICBwbG90X2x5KAogICAgdHlwZSA9ICJmdW5uZWwiLAogICAgeSA9IH5zdGVwLAogICAgeCA9IH5yZWFkcywKICAgIHRleHRpbmZvID0gInZhbHVlK3BlcmNlbnQgaW5pdGlhbCIpCgpgYGAKCiMgbm9uLWNvZGluZyBmcmFjdGlvbnMgLS0tLQpgYGB7cn0KIyByZWFkIGNvdW50CmJyZWFrZG93bl9yZWFkcyA8LSBtYXBfZGYobmFtZXMsIGZ1bmN0aW9uKGkpIHsKICByZWFkUkRTKHBhc3RlMCgiL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2RhdGEvRkMyMDI0XzA1XzAyX1BvUDk2X0JBLzAzX3pVTUlzLyIsIAogICAgICAgICAgICAgICAgIGksIAogICAgICAgICAgICAgICAgICIvelVNSXNfb3V0cHV0L3N0YXRzL09sZE5ld1BUT18iLCAKICAgICAgICAgICAgICAgICBpLCAKICAgICAgICAgICAgICAgICAiLmJyZWFrZG93bl9yZWFkY291bnQucmRzIikpICU+JQogICAgbXV0YXRlKHByb2plY3QgPSBpKQp9KQpicmVha2Rvd25fcmVhZHMgJT4lCiAgbXV0YXRlKHByb2plY3QgPSBmYWN0b3IocHJvamVjdCwgbGV2ZWxzID0gYygib2xkIiwgIm5ldyIsICJQVE8iKSkpJT4lCiAgZ2dwbG90KGFlcyh5PUZyYWN0LCB4PXByb2plY3QsIGNvbG9yPXByb2plY3QpKSArIAogIGdlb21fYm94cGxvdCgpICsKICBmYWNldF93cmFwKH50eXBlLCBzY2FsZXM9ImZyZWVfeSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSsKICB5bGFiKCJGcmFjdGlvbiBvZiBSZWFkcyIpKwogIHhsYWIoIiIpCiAgCgojIHVtaSBjb3VudApicmVha2Rvd25fdW1pIDwtIG1hcF9kZihuYW1lcywgZnVuY3Rpb24oaSkgewogIHJlYWRSRFMocGFzdGUwKCIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZGF0YS9GQzIwMjRfMDVfMDJfUG9QOTZfQkEvMDNfelVNSXMvIiwgCiAgICAgICAgICAgICAgICAgaSwgCiAgICAgICAgICAgICAgICAgIi96VU1Jc19vdXRwdXQvc3RhdHMvT2xkTmV3UFRPXyIsIAogICAgICAgICAgICAgICAgIGksIAogICAgICAgICAgICAgICAgICIuYnJlYWtkb3duX3VtaWNvdW50LnJkcyIpKSAlPiUKICAgIG11dGF0ZShwcm9qZWN0ID0gaSkKfSkKYnJlYWtkb3duX3VtaSAlPiUKICBtdXRhdGUocHJvamVjdCA9IGZhY3Rvcihwcm9qZWN0LCBsZXZlbHMgPSBjKCJvbGQiLCAibmV3IiwgIlBUTyIpKSklPiUKICBnZ3Bsb3QoYWVzKHk9RnJhY3QsIHg9cHJvamVjdCwgY29sb3I9cHJvamVjdCkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKwogIGZhY2V0X3dyYXAofnR5cGUsIHNjYWxlcz0iZnJlZV95IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpKwogIHlsYWIoIkZyYWN0aW9uIG9mIFVNSXMiKSsKICB4bGFiKCIiKQpgYGAKCgoKIyBQb3NzaWJsZSBwdWJsaWNhdGlvbiBmaWd1cmVzIC0tLS0KYGBge3J9CnNvdXJjZSgiL2hvbWUvZmVsaXgvc2NyaXB0c19hbmRfZnVuY3Rpb25zL3RoZW1lX3B1YmxpY2F0aW9uLlIiKQoKIyByZWFkIGZ1bm5lbAojZnVubmVsX3Bsb3QgPC0gcmVhZF9mdW5uZWwgJT4lIApnZ3Bsb3QoKSsKICBzdGF0X3N1bW1hcnkoZGF0YT1yZWFkX2Z1bm5lbCwgCiAgICAgICAgICAgICAgIGFlcyh5PXN0ZXAsIHg9ZnJhY3Rpb25zX3JlbF9tYXgsIGNvbG9yPXByb2plY3QpLCAKICAgICAgICAgICAgICAgZnVuLmRhdGEgPSAibWVhbl9jbF9ib290IiwgCiAgICAgICAgICAgICAgIGdlb209ImVycm9yYmFyIiwgCiAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMiksIAogICAgICAgICAgICAgICB3aWR0aD0uMiwKICAgICAgICAgICAgICAgc2l6ZT0uMikgKyAKICBnZW9tX2xpbmUoZGF0YT1yZWFkX2Z1bm5lbF9hdmcsIGFlcyh5PXN0ZXAsIHg9ZnJhY3Rpb25zX3JlbF9tYXgsIGNvbG9yPXByb2plY3QsIGdyb3VwPXByb2plY3QpKSArIAogIHNjYWxlX3hfY29udGludW91cyhwb3NpdGlvbiA9ICJ0b3AiKSArIAogIHlsYWIoIlByb2Nlc3Npbmcgc3RlcCIpICsgCiAgeGxhYigiTnVtYmVyIG9mIFJlYWRzIikrCiAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHMgPSByZXYobGV2ZWxzKHJlYWRfZnVubmVsJHN0ZXApKSkrCiAgbGFicyhjb2xvcj0iUHJvdG9jb2wiKQoKCgojIGdnc2F2ZShmdW5uZWxfcGxvdCwgCiMgICAgICAgIGZpbGVuYW1lPSIvZGF0YS9zaGFyZS9odHAvcHJpbWUtc2VxX05leHRHZW4vZmlndXJlcy9maWdfMV9mdW5uZWxfcGxvdC5wZGYiLCAKIyAgICAgICAgaGVpZ2h0PTYsIAojICAgICAgICB3aWR0aD01CiMgICAgICAgICkKCmZ1bm5lbF9wbG90IDwtIHJlYWRfZnVubmVsICU+JSAKICBtdXRhdGUoc3RlcCA9IGZhY3RvcihzdGVwLCBsZXZlbHMgPSByZXYobGV2ZWxzKHN0ZXApKSkpJT4lIAogIGdncGxvdCgpKwogIGdlb21fbGluZShhZXMoeT1zdGVwLCB4PWZyYWN0aW9ucywgY29sb3I9cHJvamVjdCkpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKHBvc2l0aW9uID0gInRvcCIpICsgCiAgeWxhYigiUHJvY2Vzc2luZyBzdGVwIikgKyAKICB4bGFiKCJSZWFkIEZyYWN0aW9uIikKCiMgZ2dzYXZlKGZ1bm5lbF9wbG90LCAKIyAgICAgICAgZmlsZW5hbWU9Ii9kYXRhL3NoYXJlL2h0cC9wcmltZS1zZXFfTmV4dEdlbi9maWd1cmVzL2ZpZ18xX2Z1bm5lbF9wbG90X3JlbGF0aXZlLnBkZiIsIAojICAgICAgICBoZWlnaHQ9NiwgCiMgICAgICAgIHdpZHRoPTUKIyAgICAgICAgKQoKCiMgdW1pIGNvdW50IHZhcmlhbmNlCnVtaV92YXJpYW5jZV9wbG90IDwtIAogIHJlYWRfdW1pX3JhdyAlPiUKICBmaWx0ZXIodHlwZSAlaW4lIGMoIkV4b24iLCAiSW50cm9uIiwgIkludHJvbitFeG9uIikpICU+JQogIGdncGxvdChhZXMoeT11bWlfY291bnQsIHg9cHJvamVjdCwgY29sb3I9cHJvamVjdCkpKwogIGdlb21fYm94cGxvdChub3RjaCA9IFQsIG91dGxpZXIuc2hhcGUgPSBOQSkrCiAgZ2VvbV9xdWFzaXJhbmRvbSgpKwogIGZhY2V0X2dyaWQofnR5cGUpKyAKICB0aGVtZV9QdWJsaWNhdGlvbigpKwogIHlsYWIoIk51bWJlciBvZiBVTUlzIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKIyBnZ3NhdmUodW1pX3ZhcmlhbmNlX3Bsb3QsIAojICAgICAgICBmaWxlbmFtZT0iL2RhdGEvc2hhcmUvaHRwL3ByaW1lLXNlcV9OZXh0R2VuL2ZpZ3VyZXMvZmlnXzJfdW1pX3ZhcmlhbmNlX3Bsb3QucGRmIiwgCiMgICAgICAgIGhlaWdodD01LCAKIyAgICAgICAgd2lkdGg9MTIKIyAgICAgICAgKQoKYGBgCgo=